package log

import (
	"context"
	"fmt"
	"io"
	"log/slog"
	"os"
	"time"

	rotatelogs "github.com/lestrrat-go/file-rotatelogs"
)

var (
	logger *slog.Logger
)

type Config struct {
	Stdout bool
	Dir    string

	Type  string
	Level string

	RotationTime  time.Duration
	RotationCount uint
}

type GetLoggerType struct {
	logger *slog.Logger
}

func init() {
	if logger == nil {
		Init(DefaultConfig())
	}
}

func DefaultConfig() *Config {
	return &Config{
		Stdout:        true,
		Dir:           "log",
		Type:          "json",
		Level:         "info",
		RotationTime:  time.Hour * 24,
		RotationCount: 3,
	}
}

func NewConfig(cfg map[string]interface{}) *Config {
	conf := DefaultConfig()

	for key, value := range cfg {
		switch key {
		case "dir":
			conf.Dir = value.(string)
		case "type":
			conf.Type = value.(string)
		case "level":
			conf.Level = value.(string)
		case "stdout":
			conf.Stdout = value.(bool)
		}
	}
	return conf
}

func Init(conf *Config) {
	logfileName := fmt.Sprintf("%s/%%Y%%m%%d.log", conf.Dir)

	rotateLog, _ := rotatelogs.New(
		logfileName,
		rotatelogs.WithRotationTime(conf.RotationTime),
		rotatelogs.WithRotationCount(conf.RotationCount),
	)

	switch conf.Type {
	case "text":
		logger = slog.New(
			slog.NewTextHandler(
				io.MultiWriter(os.Stdout, rotateLog),
				&slog.HandlerOptions{
					Level: NameToLevel(conf.Level),
				},
			),
		)
	case "json":
		logger = slog.New(
			slog.NewJSONHandler(
				io.MultiWriter(os.Stdout, rotateLog),
				&slog.HandlerOptions{
					Level: NameToLevel(conf.Level),
				},
			),
		)
	default:
		logger = slog.New(
			slog.NewTextHandler(
				io.MultiWriter(os.Stdout, rotateLog),
				&slog.HandlerOptions{
					Level: NameToLevel(conf.Level),
				},
			),
		)
	}
}

func Info(args ...interface{}) {
	logger.Info(fmt.Sprint(args...))
}

func Infof(format string, args ...interface{}) {
	logger.Info(fmt.Sprintf(format, args...))
}

func Warn(args ...interface{}) {
	logger.Warn(fmt.Sprint(args...))
}

func Warnf(format string, args ...interface{}) {
	logger.Warn(fmt.Sprintf(format, args...))
}

func Error(args ...interface{}) {
	logger.Error(fmt.Sprint(args...))
}

func Errorf(format string, args ...interface{}) {
	logger.Error(fmt.Sprintf(format, args...))
}

func Fatal(args ...interface{}) {
	logger.Log(context.Background(), slog.Level(12), fmt.Sprint(args...))
}

func Fatalf(format string, args ...interface{}) {
	logger.Log(context.Background(), slog.Level(12), fmt.Sprintf(format, args...))
}

func Trace(args ...interface{}) {
	logger.Log(context.Background(), slog.Level(-8), fmt.Sprint(args...))
}

func Tracef(format string, args ...interface{}) {
	logger.Log(context.Background(), slog.Level(-8), fmt.Sprintf(format, args...))
}

func Debug(args ...interface{}) {
	logger.Debug(fmt.Sprint(args...))
}

func Debugf(format string, args ...interface{}) {
	logger.Debug(fmt.Sprintf(format, args...))
}

func GetLogger() *GetLoggerType {
	return &GetLoggerType{
		logger: logger,
	}
}

func (l *GetLoggerType) With(args ...interface{}) *GetLoggerType {
	return &GetLoggerType{
		logger: logger.With(args...),
	}
}

func (l *GetLoggerType) WithContext(ctx context.Context, key string) *GetLoggerType {
	return &GetLoggerType{
		logger: l.logger.With(key, ctx.Value(key)),
	}
}

func (l *GetLoggerType) Info(args ...interface{}) {
	l.logger.Info(fmt.Sprint(args...))
}

func (l *GetLoggerType) Infof(format string, args ...interface{}) {
	l.logger.Info(fmt.Sprintf(format, args...))
}

func (l *GetLoggerType) Warn(args ...interface{}) {
	l.logger.Warn(fmt.Sprint(args...))
}

func (l *GetLoggerType) Warnf(format string, args ...interface{}) {
	l.logger.Warn(fmt.Sprintf(format, args...))
}

func (l *GetLoggerType) Error(args ...interface{}) {
	l.logger.Error(fmt.Sprint(args...))
}

func (l *GetLoggerType) Errorf(format string, args ...interface{}) {
	l.logger.Error(fmt.Sprintf(format, args...))
}

func (l *GetLoggerType) Fatal(args ...interface{}) {
	l.logger.Log(context.Background(), slog.Level(12), fmt.Sprint(args...))
}

func (l *GetLoggerType) Fatalf(format string, args ...interface{}) {
	l.logger.Log(context.Background(), slog.Level(12), fmt.Sprintf(format, args...))
}

func (l *GetLoggerType) Trace(args ...interface{}) {
	l.logger.Log(context.Background(), slog.Level(-8), fmt.Sprint(args...))
}

func (l *GetLoggerType) Tracef(format string, args ...interface{}) {
	l.logger.Log(context.Background(), slog.Level(-8), fmt.Sprintf(format, args...))
}

func (l *GetLoggerType) Debug(args ...interface{}) {
	l.logger.Debug(fmt.Sprint(args...))
}

func (l *GetLoggerType) Debugf(format string, args ...interface{}) {
	l.logger.Debug(fmt.Sprintf(format, args...))
}
